package client

import (
	"context"
	"fmt"
	"github.com/smallnest/rpcx/client"
	"github.com/smallnest/rpcx/protocol"
	"go.uber.org/zap"
	"sort"
	"strings"
	"sync"
	"time"
	"wzgames/internal/gloader"
	"wzgames/internal/model/login"
	"wzgames/internal/model/user"
	"wzgames/internal/response"
	"wzgames/internal/services"
	"wzgames/pkg/logger"
)

// uc rpc client

var (
	ucClient client.XClient
	ucOnce   sync.Once
)

func InitUCClient() {
	// 详见RPCx文档
	option := client.DefaultOption
	option.ConnectTimeout = 5 * time.Second
	option.Heartbeat = true
	option.HeartbeatInterval = 15 * time.Second
	option.Retries = 1
	option.SerializeType = protocol.ProtoBuffer
	d := gloader.NewServiceDiscovery(services.RPCxServiceUC)
	ucClient = client.NewXClient(services.RPCxServiceUC, client.Failfast, client.RoundRobin, d, option)
	//ucClient.SetSelector(&ucSelector{})
}

func GetUCClient() client.XClient {
	if ucClient == nil {
		ucOnce.Do(func() {
			InitUCClient()
		})
	}
	return ucClient
}

func CloseUCClient() {
	if ucClient != nil {
		ucClient.Close()
	}
}

type ucSelector struct {
	servers []string
}

func (s *ucSelector) Select(ctx context.Context, servicePath, serviceMethod string, args interface{}) string {
	switch msg := args.(type) {
	case *login.WsLoginReply:
		fmt.Println(msg.UserCache)
	}
	var ss = s.servers
	if len(ss) == 0 {
		return ""
	}

	return ss[0]
}

func (s *ucSelector) UpdateServer(servers map[string]string) {
	var ss = make([]string, 0, len(servers))
	for k := range servers {
		ss = append(ss, k)
	}

	sort.Slice(ss, func(i, j int) bool {
		return strings.Compare(ss[i], ss[j]) <= 0
	})
	s.servers = ss
}

func LoadBalance(req *user.BalanceReq) *user.BalanceRet {
	reply := &user.BalanceRet{}
	rpcCtx, cancelFn := context.WithTimeout(context.Background(), 15*time.Second)
	err := GetUCClient().Call(rpcCtx, "LoadBalance", req, reply)
	cancelFn()
	if err != nil {
		logger.Error("[UC] load balance fail", zap.Int64("AgtId", req.AgtId), zap.Uint64("UserID", req.Uid), zap.Int32("CurrID", req.CurrId), zap.Error(err))
		reply.Code = int32(response.ErrInternal)
	}

	return reply
}
